package com.zheng.coderepo.redis;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁
 * Created by zhangchaozheng on 16-7-28.
 */
public class RedisLock {

    private final static String UNLOCK_LUA = "" +
            "if redis.call(\"get\",KEYS[1]) == ARGV[1] " +
            "then " +
            "   return redis.call(\"del\",KEYS[1]) " +
            "else " +
            "   return 0 " +
            "end";

    //等待锁时间
    private final static long WAIT_LOCK_TIME = 300;

    private JedisPool jedisPool;

    public RedisLock(JedisPool jedisPool) {
        this.jedisPool = jedisPool;
    }

    /**
     * 如果分布式锁不可用, 当前线程将继续请求, 直到锁可用.
     *
     * @param lockKey 锁的键值
     * @param timeout 超时时间.单位:毫秒.
     * @return 锁的值.
     */
    public String lock(String lockKey, long timeout) {
        String lockVal = UUID.randomUUID().toString();

        for (; ; ) {
            if (!tryLock(lockKey, lockVal, timeout)) {
                try {
                    TimeUnit.MILLISECONDS.sleep(WAIT_LOCK_TIME);
                } catch (InterruptedException e) {
                }
            } else {
                break;
            }
        }
        return lockVal;
    }

    /**
     * 尝试获得分布式锁, 如果锁可用, 返回true
     *
     * @param lockKey 锁的键值
     * @param value   锁的值,推荐使用随机字符.
     * @param timeout 超时时间.单位:毫秒.
     * @return true/false
     */
    public boolean tryLock(String lockKey, String value, long timeout) {
        Jedis jedis = jedisPool.getResource();
        try {
            Object result = jedis.set(lockKey, value, "NX", "PX", timeout);
            return "OK".equalsIgnoreCase(result + "");
        } finally {
            quietlyCloseJedis(jedis);
        }
    }

    /**
     * 释放锁.
     *
     * @param lockKey 锁的键值
     * @param value   锁的值.必须要和上锁的值一致.
     */
    public void unlock(String lockKey, String value) {
        Jedis jedis = jedisPool.getResource();
        try {
            jedis.eval(UNLOCK_LUA, 1, lockKey, value);
        } finally {
            quietlyCloseJedis(jedis);
        }
    }

    /**
     * 关闭redis连接。jedis 3.0开始，官方抛弃了原来的returnResource和returnBrokenResource。
     *
     * @param jedis
     */
    private void quietlyCloseJedis(Jedis jedis) {
        if (jedis != null) {
            jedis.close();
        }
    }

}
